// ***************************************************************************************
// Copyright (c) 2023-2025 Peng Cheng Laboratory
// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences
// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip
//
// iEDA is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//
// See the Mulan PSL v2 for more details.
// ***************************************************************************************
/**
 * @File Name: contest_process.cpp
 * @Brief :
 * @Author : Yell (12112088@qq.com)
 * @Version : 1.0
 * @Creat Date : 2023-09-15
 *
 */

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "contest_process.h"
#include "STA_interface.h"
#include "Route.h"
#include <exception>
#include "contest_dm.h"
#include "contest_util.h"
#include "idm.h"
#include <unordered_map>
#include "contest_coord.h"
#include <iostream>
#include <cstdlib>
#include "./Third/CoordEncoding/CoordinateCodec.hpp"

#include "OptimalRegion.h"
#include "FinalRegion.h"
#include <chrono>
#include "Move.h"

namespace idb {
class IdbLayout;
class IdbDesign;
}  // namespace idb

namespace ieda_contest {

ContestProcess::ContestProcess(ContestDataManager* data_manager)
{
  ///////////////////////////全流程////////////////////////////////////////////
  cell_move_overall_flow(data_manager);
  ///////////////////////////全流程////////////////////////////////////////////


  // ///////////////////////////测试codeC////////////////////////////////////////////
  // TestingCodeC();
  // ///////////////////////////测试codeC////////////////////////////////////////////


  // bool overflowFlag;
  
  // // 加载datamanager
  // _data_manager = data_manager;
  // myContestEvaluation contest_evaluation(_data_manager);

  // // zhihui test OptimalRegion.cpp and FinalRegion.cpp
  // OptimalRegion myOptimalRegion(_data_manager);
  // FinalRegion myFinalRegion(_data_manager);
  // for (idb::IdbInstance* CellPtr : _data_manager->get_idb_design()->get_instance_list()->get_instance_list()) {
    
  //   // ---------------test code-----------------
  //   // count计数器，打印 RelativeNetsMap 的前10条记录
  //   // int count = 0;
  //   // for (auto &entry : _data_manager->get_database()->getRelativeNetsMap()) {
  //   //   idb::IdbInstance *instance = entry.first;
  //   //   std::vector<ContestNet *> &nets = entry.second;

  //   //   std::cout << "Instance: " << instance->get_name() << std::endl;
  //   //   std::cout << "Connected Nets: ";
  //   //   for (ContestNet *net : nets) {
  //   //     std::cout << net->get_net_name() << " ";
  //   //   }
  //   //   std::cout << std::endl;

  //   //   ++count;
  //   //   if (count >= 50) {
  //   //     break;
  //   //   }

  //   // }
  //   // ---------------test code-----------------

  //   myOptimalRegion.getRegion(CellPtr);

  //   // std::unordered_map<ContestCoord, int, std::hash<ContestCoord>> _2D_Supply_map = myFinalRegion.cal_2D_Supply_map();
  //   // std::unordered_map<idb::IdbInstance *, std::pair<int, int>> FinalPos = myFinalRegion.calFinalPos();
  //   // myFinalRegion.getRegion(CellPtr);
  // }

  // // 抽取single cell并且查看
  // std::unordered_map<ContestCoord, SingleGcellInfo, std::hash<ContestCoord>>& contest_singleGcell_map = _data_manager->get_database()->get_singleGcell_map();
  // int count = 0; // 初始化计数器
  // int totalDemand = 0;
  // int maxDemand = 0;
  // for (const auto& pair : contest_singleGcell_map) {
  //   // if (count >= 10000) break;

  //   const ContestCoord& coord = pair.first; // 键（ContestCoord对象）
  //   const SingleGcellInfo& gcellInfo = pair.second; // 值（SingleGcellInfo对象）
  //   if (coord.get_x() == 0 && coord.get_y() == 0) {
  //     std::cout << "ContestCoord(x=" << coord.get_x() 
  //               << ", y=" << coord.get_y() 
  //               << ", layer_idx=" << coord.get_layer_idx() 
  //               << ") -> SingleGcell Capacity/Demand: " 
  //               << gcellInfo.get_cell_capacity() 
  //               << " / "
  //               << gcellInfo.get_cell_demand()
  //               << std::endl;      
  //   }


  //   count++; 
  //   totalDemand += gcellInfo.get_cell_demand();
  //   if (gcellInfo.get_cell_demand() > maxDemand) {
  //       maxDemand = gcellInfo.get_cell_demand(); // 更新最大demand
  //   }
  // }
  // std::cout << "Total number of coordinates: " << totalDemand << std::endl;
  // std::cout << "Max Demand: " << maxDemand << std::endl;

  // overflowFlag = contest_evaluation.overflowCheckPassed();

  // std::cout << "Overflow check pass? " << overflowFlag << std::endl;

  // ContestNet test_net = _data_manager->get_database()->get_net_list()[60000];

  // /*
  // int length = test_net.get_net_length();
  // std::vector<ContestGuide>& myguide = test_net.get_guide_list();


  // if (contest_evaluation.connectivityCheckPassed()) {
  //   std::cout << "Connectivity check successful!" << std::endl;
  // } else {
  //   std::cout << "Connectivity check failed!" << std::endl;
  // }
  // */
  // // contest_evaluation.printWnsAndTNS();
  // // std::cout << "WNS and TNS, Done!" << std::endl;

  // // guangxi
  // Route Route_do(_data_manager);

  // // 测试输入x,y,layerID, 输出Gcell
  // // for (int i = 0; i < 100; ++i) {
  // //     // 随机生成x, y和layerID
  // //     int x = std::rand() % 51; // 0到50之间的数
  // //     int y = std::rand() % 51; // 0到50之间的数
  // //     int L = std::rand() % 8;  // 0到7之间的数

  // //     try {
  // //         SingleGcellInfo& cell = Route_do.FindCellInMap(x, y, L);
  // //         std::cout << "Test " << i+1 << ": The capacity of the found SingleGcell at (x=" << x << ", y=" << y << ", layerID=" << L << ") is: " << cell.get_cell_capacity() << std::endl;
  // //     } catch (const std::exception& e) {
  // //         std::cerr << "Test " << i+1 << ": Exception caught for (x=" << x << ", y=" << y << ", layerID=" << L << "), " << e.what() << std::endl;
  // //     }
  // // }

  // Route_do.processRoute();
  // Route_do.addNetLength();
  // Route_do.rerouteAll();
  // Route_do.processRoute();


  // count = 0;
  // for (ContestInstance& contest_instance : _data_manager->get_database()->get_instance_list()) {
  //   std::string& insName = contest_instance.get_name();
  //   ContestCoord& realCoord = contest_instance.get_real_coord();
  //   int real_x = realCoord.get_x();
  //   int real_y = realCoord.get_y();
  //   int real_layer_index = realCoord.get_layer_idx();
  //   int real_area = contest_instance.get_area();

  //   ContestCoord& gridCoord = contest_instance.get_grid_coord();
  //   int grid_x = gridCoord.get_x();
  //   int grid_y = gridCoord.get_y();
  //   int grid_layer_index = gridCoord.get_layer_idx();
  //   int grid_area = contest_instance.get_area();

  //   if (count <= 10) {
  //     std::cout << std::endl;
  //     std::cout << insName << std::endl;
  //     std::cout << "information for the " << count <<  " instance:" << std::endl;
  //     std::cout << "real x = " << real_x << ";" << std::endl;
  //     std::cout << "real y = " << real_y << ";" << std::endl;
  //     std::cout << "grid x = " << grid_x << ";" << std::endl;
  //     std::cout << "grid y = " << grid_y << ";" << std::endl;
  //     std::cout << "real area = " << real_area << ";" << std::endl;
  //     std::cout << "grid area = " << grid_area << ";" << std::endl;
  //     std::cout << "real layer index:  = " << real_layer_index << ";" << std::endl;
  //     std::cout << "grid layer index:  = " << grid_layer_index << "." << std::endl;
  //     std::cout << std::endl;
  //   }
  //   count = count + 1;

  // }

}

void ContestProcess::TestingCodeC() {
  std::cout << "lets test CodeC!" << std::endl;

  int MaxR = 11;
  int MaxC = 9;
  int MaxL = 5;
  int MinR = 1;
  int MinC = 1;
  int MinL = 1;
  std::vector<int> Coord1 = {9, 8, 3};
  std::vector<int> Coord2 = {3, 2, 4};

  cell_move_router::CoordinateCodec<unsigned long long> Codec(
      {(unsigned long long)(MaxR - MinR + 1),
       (unsigned long long)(MaxC - MinC + 1),
       (unsigned long long)(MaxL - MinL + 1)});
  
  size_t Coord_1_encoding = Codec.encode(
      {(unsigned long long)Coord1[0],
      (unsigned long long)Coord1[1],
      (unsigned long long)Coord1[2]});

  std::cout << Coord_1_encoding << std::endl;
  std::cout << Codec.max() << std::endl;
}

void ContestProcess::cell_move_overall_flow(ContestDataManager* data_manager) {
  
  // initialize datamanager, route and move
  _data_manager = data_manager;
  myContestEvaluation contest_evaluation(_data_manager);

  Route Route_do(_data_manager);
  Route_do.addNetLength();
  Route_do.initialCurrentCost();

  Move Move_do(_data_manager);

  auto startTime_init = std::chrono::high_resolution_clock::now();
  long long OriginalCost = Route_do.get_currentCost();

  // 1. Reroute First, also make sure there's no Open Net
  Route_do.rerouteAll();

  std::cerr << "Original:\n";
  std::cerr << "  CurrentCost: " << OriginalCost * 0.0001 << '\n';
  long long FirstRerouteCost = Route_do.get_currentCost();
  std::cerr << "After re-route:\n";
  std::cerr << "  CurrentCost: " << FirstRerouteCost * 0.0001 << '\n';
  std::cerr << "  ReducedCost: " << (OriginalCost - FirstRerouteCost) * 0.0001 << '\n';
  auto endTime = std::chrono::high_resolution_clock::now();
  std::cout << "reroute time: "<< std::chrono::duration_cast<std::chrono::duration<float>>(endTime - startTime_init).count() << '\n' ;
  // auto moveStartTime = std::chrono::high_resolution_clock::now();

  // 全流程，用线长作为TNS的estimator，优化线长可以优化TNS
  // step1: 预处理，微调初始布线
  // step2: 移动cell，再把cell涉及到的net重新布线
  // step3: 后处理，再一次微调布线
  // 注：由于时间控制在1h，目前我们的代码运行时间太长且还没有进行多线程处理
  // 对于我们来说，目前只能运行部分step1，并且得到一定的结果优化


  // 2. Construct an Optimal Region For Cell Movement
  // OptimalRegion myOptimalRegion(_data_manager);
  // // 理论上要用FinalRegion::getRegion。
  // FinalRegion myFinalRegion(_data_manager);

  // 3. Move Cell Based on Optimal Region
  // Flute compile problem
  // Move_do.move(myFinalRegion, startTime_init);
  // Move_do.move(myOptimalRegion, startTime_init);

  // long long MoveCost = Route_do.get_currentCost() + _data_manager->get_database()->getMoveCost();
  // std::cerr << "After move:\n";
  // std::cerr << "  CurrentCost: " << MoveCost * 0.0001 << '\n';
  // std::cerr << "  ReducedCost: " << (FirstRerouteCost - MoveCost) * 0.0001 << '\n';
  // auto moveEndTime = std::chrono::high_resolution_clock::now();
  // std::cout << "move time: "<< std::chrono::duration_cast<std::chrono::duration<float>>(moveEndTime - moveStartTime).count() << '\n' ;
  // auto secondRerouteStartTime = std::chrono::high_resolution_clock::now();

  // 4. Reroute Again to further reduce total wirelength
  // 这部分的Cost赋值要修改
  // Route_do.rerouteAll();

  // long long RerouteCost = MoveCost;
  // std::cerr << "After re-route:\n";
  // std::cerr << "  CurrentCost: " << RerouteCost * 0.0001 << '\n';
  // std::cerr << "  ReducedCost: " << (MoveCost - RerouteCost) * 0.0001 << '\n';
  // auto secondRerouteEndTime = std::chrono::high_resolution_clock::now();
  // std::cout << "reroute time: "<< std::chrono::duration_cast<std::chrono::duration<float>>(secondRerouteEndTime - secondRerouteStartTime).count() << '\n' ;

  // 测试代码
  // std::string testingNet = "FE_OFN1644_n87518";
  // std::cout << "After Route in DataManager" << std::endl;
  // for (ContestNet& netTesting : _data_manager->get_database()->get_net_list()) {
  //     if (netTesting.get_net_name() == testingNet) {
  //         for (ContestSegment originSegment : netTesting.get_routing_segment_list()) {
  //             ContestCoord coord_start = originSegment.get_first();
  //             ContestCoord coord_end = originSegment.get_second();
  //             std::cout << "Start Points of Original Routing Segment: x = " << coord_start.get_x() 
  //             << ", y = " << coord_start.get_y() << ", L = " << coord_start.get_layer_idx() << std::endl;
  //             std::cout << "End Points of Original Routing Segment: x = " << coord_end.get_x() 
  //             << ", y = " << coord_end.get_y() << ", L = " << coord_end.get_layer_idx() << std::endl;
  //         }
  //     }
  // }

}

}  // namespace ieda_contest
